import java.applet.Applet;
import java.applet.AudioClip;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Image;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;
import javax.swing.JOptionPane;

public class main extends Applet implements Runnable {
	/**
	 * 
	 * 	Created by J. Siadis
	 * 
	 *  Used the tutorial from http://www.angelfire.com/games4/anirak/tutorial/day1/ as a starter
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	//double buffering
	Graphics gBuf;
	Image imgBuf;
	
	//screen size
	static final int WIDTHSCREEN  = 600;
	static final int HEIGHTSCREEN = 600;
	
	//character size
	static final int CHARACTER_SIZE = 20;
	
	//set the maze size
	static final int MAZE_SIZE = 30;
	
	//used for calculations
	int x = 0;
	int y = 0;
	int row;
	int col;
	int showExit;
	
	//for movement
	int move_x = 0;
	int move_y = 0;
	
	//used for the scores
	int totalCoins = 0;
	int totalScore = 0;
	int totalLives = 3;
	
	//The mainThread
	Thread mainThread;
	long startTime; 
	
	//used for collisionDetection(CD)
	boolean collision;
	boolean endGame;
	
	maze maze_;
	
	//used for preprocessing
	ArrayList<objects> fieldArray;
	ArrayList<npc> npcArray;
	
	//for the sound
	Sequencer sequencer;
	boolean playMain = true;
	
	//size 30 x 30
	//demo playfield
	/*
	int[][] playField =
	{ 	{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,1,3,3,4,1,0,1,3,3,3,1,0,0,0,0,0,0,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,1,3,3,3,1,0,1,3,3,3,1,0,0,6,0,0,0,0,0,0},
		{0,0,1,1,1,1,1,1,0,0,1,1,0,1,1,1,1,1,0,1,1,0,0,0,0,0,0,0,0,0},
		{0,0,1,3,3,3,7,1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,7,0,0,0,0,0,0},
		{0,0,1,3,1,1,1,1,1,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,0,1,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,9,9,9,0,0,0,0,0},
		{0,0,1,3,3,3,3,1,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,8,9,0,0,0,0,0},
		{0,0,1,1,1,1,1,1,0,0,0,0,0,0,1,0,0,0,4,0,0,0,9,9,9,0,0,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,0,1,3,3,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,0,0,5,0,0,0,0,0,0,0,1,3,3,3,1,0,0,0,0,1,1,1,1,1,0,0,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,3,3,1,0,0,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,3,1,0,0,0,0,0},
		{0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,2,1,0,0,0,0,0},
		{0,0,0,0,1,0,1,1,1,1,0,0,0,0,6,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0},
		{0,0,0,0,1,3,3,3,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,0,0,0,1,3,3,3,3,1,0,0,0,0,0,0,0,1,0,0,0,1,1,1,1,0,1,0,0,0},
		{0,0,0,0,1,1,1,1,1,1,0,0,7,0,0,0,0,1,0,0,0,1,4,0,1,0,1,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,1,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,1,1,1,0,1,1,1,0},
		{0,0,1,1,1,1,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,0,1,3,3,1,0,1,4,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,0,1,3,3,1,0,1,3,3,1,0,0,0,0,0,1,0,0,0,0,0,4,0,0,0,0,0,0,0},
		{0,0,1,1,0,1,0,1,0,1,1,0,0,0,0,0,1,0,1,1,1,0,0,0,0,0,0,0,0,0},
		{0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,1,3,3,3,1,0,0,0,0,0,0,0,0,0},
		{0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,1,3,3,3,1,0,0,0,0,0,0,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0},
	};
	*/
	
	///default playField
	int[][] playField =
	{ 	{0,1,7,1,7,4,0,0,0,0,0,1,2,1,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,1,4,1,1,1,1,1,0,1,0,1,4,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{7,1,0,0,0,8,0,0,0,1,0,1,1,1,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,1,1,1,0,1,1,1,1,1,0,1,7,1,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,4,0,0,0,1,0,0,0,0,0,1,0,1,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{1,1,1,1,0,1,0,1,1,1,1,1,0,1,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{7,4,0,0,0,1,0,0,0,0,0,0,0,1,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{1,1,0,1,0,1,0,1,1,1,0,1,1,1,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
		{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
	};
	
	/**
	 * 
	 * initialize the screen size, background, buffering, the fieldArray & npcArray
	 * 
	 */
    public void init()
    {
    	System.out.println("initialize");
    	resize(WIDTHSCREEN, HEIGHTSCREEN);
    	System.gc();
    	setBackground(Color.green);
    	requestFocus();
    	
    	imgBuf = createImage(WIDTHSCREEN, HEIGHTSCREEN);
    	gBuf = imgBuf.getGraphics();
    	
    	fieldArray = new ArrayList<objects>();
    	npcArray = new ArrayList<npc>();
    	
    	 try {
			Sequence sequence = MidiSystem.getSequence(new File("sounds/midi/mmxpass2.mid"));
			sequencer = MidiSystem.getSequencer();
		    sequencer.open();
		    sequencer.setSequence(sequence);
		    sequencer.start();
		} catch (InvalidMidiDataException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (MidiUnavailableException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    }
    
    /**
     * 
     * is being called by the repaint() method (see run)
     * 
     */
    public void update(Graphics g) {
        g.drawImage(imgBuf, 0, 0 ,this);
    }
    
    /**
     * 
     * start the (main)thread
     * 
     */
    public void start(){
    	mainThread = new Thread(this, "GAME");
    	mainThread.start();
    	startTime = System.currentTimeMillis();
    }
    
    /**
     * 
     * end the (main)thread
     * 
     */
    public void stop()
    {
    	mainThread = null;
    }

    /**
     * 
     * the thread runs constantly and draws the whole playfield
     * 
     */
	@Override
	public void run() {
		System.out.println("run thread");
		
		double eye = 7;
		Component parentComponent = null;
		showExit = 0;
		totalCoins = 0;
		
		//The preprocessing
		maze_ = new maze(MAZE_SIZE, col, row, CHARACTER_SIZE, playField, gBuf, (int) eye);
		fieldArray = maze_.preprocessMaze();
		npcArray = maze_.preprocessNPC();

		while(mainThread != null)
		{
			try
			{
				//Slows the applet down for very fast machines
				long sleepTime = Math.max(startTime - System.currentTimeMillis(), 20);
				mainThread.sleep(sleepTime);
			}
			catch (InterruptedException e)
			{
			}
			
			int nextX;
			int nextY;
			
			nextX = x + move_x;
			nextY = y + move_y;
			collision = false;
			endGame = false;
			
			//handles collision detection
			try {
				collisionDetection(nextX, nextY, parentComponent);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			
			gBuf.clearRect(0, 0, WIDTHSCREEN, HEIGHTSCREEN);
			
			//
			//movement handling
			//
			if(collision == false)
			{
				x = nextX;
				y = nextY;
			}
			
			if(x >= WIDTHSCREEN - CHARACTER_SIZE)
			{
				x = WIDTHSCREEN - CHARACTER_SIZE;
				move_x = 0;
			}
			if(x <= 0)
			{
				x = 0;
				move_x = 0;
			}
			if(y >= HEIGHTSCREEN - CHARACTER_SIZE)
			{
				y = HEIGHTSCREEN - CHARACTER_SIZE;
				move_y = 0;
			}
			if(y <= 0)
			{
				y = 0;
				move_y = 0;
			}
			
			eye = eye + 0.1;
			
			if(eye > 10)
			{
				eye = 7;
			} 
			
			//draw the maze objects
			for(int a = 0; a < fieldArray.size(); a++)
			{
				fieldArray.get(a).draw(gBuf);
			}
			
			//draw the enemies and move the eyes
			for(int a = 0; a < npcArray.size(); a++)
			{
				npcArray.get(a).drawNPC();
				npcArray.get(a).setEye((int)eye);
			}

			//draw the scoreboard
			gBuf.setColor(Color.BLACK);
			gBuf.drawString("total lives = " + totalLives, WIDTHSCREEN - 170,12);
			gBuf.drawString("total coins = " + totalCoins, WIDTHSCREEN - 90,12);
			gBuf.drawString("total score = " + totalScore, WIDTHSCREEN - 263,12);
			
			//draw the maincharacter
			drawCharacter(gBuf, x, y, eye);
			
			repaint();
		}
	}

	/**
	 * 
	 * draw the main character
	 * 
	 */
	private void drawCharacter(Graphics gBuf2, int x2, int y2, double eye) {
		
		//draw the body
		gBuf.setColor(Color.BLACK);
		gBuf.fillRect(x, y, CHARACTER_SIZE, CHARACTER_SIZE);
		
		//draw the eyes
		gBuf.setColor(Color.WHITE);
		gBuf.fillRect(x + 3, y + 1, 4, (int) eye);
		gBuf.fillRect(x + 11, y + 1, 4, (int) eye);
	}

	/**
	 * 
	 * when pressing a key down
	 * 
	 */
	public boolean keyDown(java.awt.Event e, int key)
	{
		System.out.println(key);

		if (key == 1006) // left arrow
		{
		   	move_x = -2;
		   	move_y = 0;
			return false;
		}
		if (key == 1007) // right arrow
		{
	 		move_x = 2;
	 		move_y = 0;
		    return false;
		}

		if (key == 1004) // up arrow
		{
		  	move_y = -2;
		  	move_x = 0;
			return false;
		}
		if (key == 1005) // down arrow
		{
		 	move_y = 2;
		 	move_x = 0;
			return false;
		}

		if (key == 32) // space bar
		{
			System.out.println("SPACEBAR");
			return false;
		}

		if (key == 112) // 'P' key
		{
			System.out.println("PAUSE");
			return false;
		}
		
		if (key == 115) // 'S' key
		{
			System.out.println("sound on/off");
			
			if(playMain)
			{
				sequencer.stop();
				playMain = false;
			}
			else
			{
				sequencer.start();
				playMain = true;
			}
			
			return false;
		}

		return false;
	}
	
	/**
	 * 
	 * when releasing a key
	 * 
	 */
	public boolean keyUp(java.awt.Event e, int key)
	{
		if (key == 1006 || key == 1007)  // left or right key released
		{
			move_x = 0;
			return false;
		}

		if (key == 1004 || key == 1005) // up or down key released.
		{
			move_y = 0;
			return false;
		}

		if (key == 32)  // space bar released
		{
			return false;
		}
		return false;
	}
	
	/**
	 * 
	 * the collisionDetection which uses the array to detect the various objects
	 * 
	 * @param nextX
	 * @param nextY
	 * @param parentComponent
	 * @throws InterruptedException
	 */
	public void collisionDetection(int nextX, int nextY, Component parentComponent) throws InterruptedException
	{	
		for (int xCorner = 0; xCorner < CHARACTER_SIZE; xCorner += CHARACTER_SIZE - 1)
		{		
			for (int yCorner = 0; yCorner < CHARACTER_SIZE; yCorner += CHARACTER_SIZE - 1)
			{	
				col = (nextX + (xCorner)) / CHARACTER_SIZE;
				row = (nextY + (yCorner)) / CHARACTER_SIZE;
				
				if (row < MAZE_SIZE && col < MAZE_SIZE)
				{								
					//collide against walls
					if (playField[row][col] == 1 || playField[row][col] == 9 || playField[row][col] == 5)
					{
						collision = true;
						break;
					}
					
					//finding the exit 
					if (playField[row][col] == 2)
					{
						AudioClip sound = getAudioClip( getDocumentBase(), "sounds/goal.wav" );
						sound.play();
						
						JOptionPane.showMessageDialog(parentComponent, "You found the exit! \nNext level","winner", JOptionPane.WARNING_MESSAGE);
						System.exit(0);
					}
					
					//collide against an enemy
					if (playField[row][col] == 4)
					{	
						System.out.println("The col = " + col);
						System.out.println("nextX = " + nextX);
						System.out.println("The row = " + row);
						System.out.println("nextY = " + nextY);
						
						move_y = 0;
						move_x = 0;
						
						System.out.println("NPC");
						
						checkNPCArray(npcArray, col, row);
						
						//must give the right answer
						//for every wrong answer a live will be lost
						JOptionPane.showInputDialog(parentComponent, "dummy1", "enter youre name");
						playField[row][col] = 0;
						collision = true;
						break;
					}
					
					//collide against a coin
					if (playField[row][col] == 7)
					{	
						checkFieldArray(fieldArray, col, row);
						
						playField[row][col] = 0;
						totalCoins = totalCoins + 1;
						totalScore = totalScore + 20;
						showExit = totalCoins;
					
						AudioClip sound = getAudioClip( getDocumentBase(), "sounds/push.wav" );
						sound.play();
						
						break;
					}
					
					//collide against a fireOrb
					if (playField[row][col] == 8)
					{
						checkFieldArray(fieldArray, col, row);
						
						playField[row][col] = 0;
						totalLives = totalLives + 1;
						totalScore = totalScore + 50;
						showExit = totalCoins;
						
						AudioClip sound = getAudioClip( getDocumentBase(), "sounds/push.wav" );
						sound.play();
						
						break;
					}
				}
			}
		}
	}

	private void checkNPCArray(ArrayList<npc> array, int col, int row) {
		
		for(int a = 0; a < array.size(); a++)
		{
			if(array.get(a).getCol() == col && array.get(a).getRow() == row)
			{
				array.get(a).setDraw(false);
			}
		}
		
	}
	
private void checkFieldArray(ArrayList<objects> array, int col, int row) {
		
		for(int a = 0; a < array.size(); a++)
		{
			if(array.get(a).getCol() == col && array.get(a).getRow() == row)
			{
				array.get(a).setDraw(false);
			}
		}
		
	}

}